//*********************************************************
//
//    Copyright (c) Microsoft. All rights reserved.
//    This code is licensed under the Apache License, Version 2.0.
//    THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
//    ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
//    IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
//    PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//********************************************************* 

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Runtime.InteropServices;
using Microsoft.Office.Core;
using Microsoft.TridentWordAddIn.Common;
using Microsoft.TridentWordAddIn.ResourceHelper;

namespace Microsoft.TridentWordAddIn.Presentation
{
    /// <summary>
    /// The ribbon extensibility of the Trident Package
    /// Class must be declared as public on order for Word to be able to show the ribbon defined in here.
    /// </summary>
    [ComVisible(true)]
    public class TridentRibbon : IRibbonInvalidator, IRibbonExtensibility
    {
        #region Fields

        private readonly IController controller;
        private IRibbonUI ribbonUI;
        private List<TridentConnection> connectionsList;
        private bool invalidateRibbon = true;

        #endregion

        /// <summary>
        /// Initializes a new instance of the <see cref="TridentRibbon"/> class.
        /// </summary>
        /// <param name="controller">The controller.</param>
        internal TridentRibbon(IController controller)
        {
            if (controller == null)
            {
                throw new ArgumentNullException("controller");
            }

            this.controller = controller;
            this.controller.InitializeBaseRibbonInvalidator(this);
        }

        #region IRibbonExtensibility Members

        /// <summary>
        /// Gets the custom UI.
        /// </summary>
        /// <param name="RibbonID">The ribbon ID.</param>
        /// <returns>string</returns>
        public string GetCustomUI(string RibbonID)
        {
            return ResourceFetcher.GetString("TridentRibbon");
        }

        #endregion

        #region IRibbonValidator Members

        /// <summary>
        /// Invalidates the control.
        /// </summary>
        /// <param name="controlId">The control id.</param>
        public void InvalidateControl(string controlId)
        {
            try
            {
                this.ribbonUI.InvalidateControl(controlId);
            }
            catch (COMException)
            {
                // these can happen on shutdown.  we don't care.
            }
        }

        /// <summary>
        /// Invalidates this instance.
        /// </summary>
        public void Invalidate()
        {
            this.ribbonUI.Invalidate();
        }

        #endregion

        #region Ribbon Callbacks

        /// <summary>
        /// Called when ribbon extension loads.
        /// </summary>
        /// <param name="ribbonUIInstance">The ribbon UI instance.</param>
        public void OnLoad(IRibbonUI ribbonUIInstance)
        {
            this.ribbonUI = ribbonUIInstance;
        }

        /// <summary>
        /// Callback for getting the label for a ribbon control.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>string</returns>
        public string GetLabel(IRibbonControl ribbonControl)
        {
            string result = string.Empty;

            if (ribbonControl == null)
            {
                Trace.WriteLine("ribbonControl is null", TraceLevel.Error.ToString());
                throw new ArgumentNullException("ribbonControl");
            }

            string labelResourceName = ribbonControl.Id + "Label";
            result = GetResourceString(labelResourceName);

            return result;
        }

        /// <summary>
        /// The callback for loading a named image for a ribbon control.
        /// </summary>
        /// <param name="imageName">Name of the image.</param>
        /// <returns>Bitmap</returns>
        public Bitmap LoadImage(string imageName)
        {
            return ResourceFetcher.GetImage(imageName) as Bitmap;
        }

        /// <summary>
        /// The callback for getting the screen tip for a ribbon control.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>string</returns>
        public string GetScreenTip(IRibbonControl ribbonControl)
        {
            string result = string.Empty;

            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            string screenTipResourceName = ribbonControl.Id + "ScreenTip";
            result = GetResourceString(screenTipResourceName);
            return result;
        }

        /// <summary>
        /// Gets the enabled.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>bool</returns>
        public bool GetEnabled(IRibbonControl ribbonControl)
        {
            if (this.controller.DocumentsCount > 0)
            {
                if (!this.invalidateRibbon)
                {
                    this.ribbonUI.Invalidate();
                }

                this.invalidateRibbon = true;
                return true;
            }

            if (this.invalidateRibbon)
            {
                this.ribbonUI.Invalidate();
                this.invalidateRibbon = false;
            }

            return false;
        }

        /// <summary>
        /// Gets the enabled.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>bool</returns>
        public bool ComboBoxGetEnabled(IRibbonControl ribbonControl)
        {
            if (this.controller.DocumentsCount > 0 && this.controller.UserData.Connections.Count > 0)
            {
                if (!this.invalidateRibbon)
                {
                    this.ribbonUI.Invalidate();
                }

                this.invalidateRibbon = true;
                return true;
            }

            if (this.invalidateRibbon)
            {
                this.ribbonUI.Invalidate();
                this.invalidateRibbon = false;
            }

            return false;
        }

        /// <summary>
        /// Determines whether [is connection present] [the specified ribbon control].
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>
        /// 	<c>true</c> if [is connection present] [the specified ribbon control]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsConnectionPresent(IRibbonControl ribbonControl)
        {
            return this.controller.IsConnectionPresent();
        }

        /// <summary>
        /// Determines whether [is associations present].
        /// </summary>
        /// <returns>
        /// 	<c>true</c> if [is associations present]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsAssociationsPresent(IRibbonControl ribbonControl)
        {           
            return this.controller.IsAssociationsPresent();
        }  

        /// <summary>
        /// Determines whether [is associations present] [the specified ribbon control].
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>
        /// 	<c>true</c> if [is associations present] [the specified ribbon control]; otherwise, <c>false</c>.
        /// </returns>
        public bool IsConnectionAndAssociationsPresent(IRibbonControl ribbonControl)
        {
            return this.controller.IsConnectionAndAssociationsPresent();
        }      

        /// <summary>
        /// BTNs the dashboard_ on action.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void DashboardButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand dashboardCommand = new DashboardCommand(this.controller);
            dashboardCommand.Invoke(isPressed);
            this.CheckForNeedToInvalidate(dashboardCommand, ribbonControl.Id);
        }

        /// <summary>
        /// On action callback for the ViewsAssociations toggle button .
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void ViewWorkflowAssociationsButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand viewWorkflowAssociationCommand = new ViewWorkflowAssociationCommand(this.controller);
            viewWorkflowAssociationCommand.Invoke(isPressed);
            this.CheckForNeedToInvalidate(viewWorkflowAssociationCommand, ribbonControl.Id);
        }

        /// <summary>
        /// on action callback for  viewing the connectionsList callback
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void ViewConnectionsButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand command = new ViewConnectionsCommand(this.controller);
            command.Invoke(isPressed);
            this.CheckForNeedToInvalidate(command, ribbonControl.Id);
        }

        /// <summary>
        /// Associates the Workflow button on action.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void AssociateWorkflowButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand associateWorkflowCommand = new AssociateWorkflowCommand(this.controller);
            associateWorkflowCommand.Invoke(isPressed);
            this.CheckForNeedToInvalidate(associateWorkflowCommand, ribbonControl.Id);
        }

        /// <summary>
        /// Protects the document button on action.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void ProtectDocumentButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand command = new DocumentProtectionCommand(this.controller);
            command.Invoke(ribbonControl.Id);
            this.CheckForNeedToInvalidate(command, ribbonControl.Id);
        }

        /// <summary>
        /// Genes the pattern link button on action.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void InsertTridentLinkButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand insertTridentLinkCommand = new InsertTridentLinkCommand(this.controller);
            insertTridentLinkCommand.Invoke(isPressed);
            this.CheckForNeedToInvalidate(insertTridentLinkCommand, ribbonControl.Id);
        }

        /// <summary>
        /// Reruns the Workflow button on action.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void RunWorkflowButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand rerunWorkflowCommand = new RunWorkflowCommand(this.controller);
            rerunWorkflowCommand.Invoke(isPressed);
            this.CheckForNeedToInvalidate(rerunWorkflowCommand, ribbonControl.Id);
        }

        /// <summary>
        /// Reruns the Workflow button on action.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="isPressed">if set to <c>true</c> [is pressed].</param>
        public void RerunWorkflowButtonOnAction(IRibbonControl ribbonControl, bool isPressed)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand rerunWorkflowCommand = new RerunWorkflowCommand(this.controller);
            rerunWorkflowCommand.Invoke(isPressed);
            this.CheckForNeedToInvalidate(rerunWorkflowCommand, ribbonControl.Id);
        }

        /// <summary>
        /// The onAction callback for the New connection button.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        public void NewConnectionButtonOnAction(IRibbonControl ribbonControl)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            RibbonCommand command = new CreateConnectionCommand(this.controller);
            command.Invoke(ribbonControl.Id);
            this.CheckForNeedToInvalidate(command, ribbonControl.Id);
        }

        /// <summary>
        /// Callback for determinig the pressed state of a ribbon toggle button.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>bool</returns>
        public bool ToggleButtonGetPressed(IRibbonControl ribbonControl)
        {
            if (ribbonControl == null)
            {
                throw new ArgumentNullException("ribbonControl");
            }

            GetButtonPressedCommand getPressedCommand = new GetButtonPressedCommand(this.controller);
            return getPressedCommand.GetButtonPressed(ribbonControl.Id);
        }

        /// <summary>
        /// Connectionses the drop down on action.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="selectedValue">The selected value.</param>
        /// <param name="selectedIndex">Index of the selected.</param>
        public void ConnectionsDropDownOnAction(IRibbonControl ribbonControl, string selectedValue, int selectedIndex)
        {
            try
            {
                if (ribbonControl == null)
                {
                    throw new ArgumentNullException("ribbonControl");
                }

                this.SetConnectionFormIndex(selectedIndex);
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowException(ex, null);
            }
        }

        /// <summary>
        /// Getconnectionses the drop down item count.
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>int</returns>
        public int GetConnectionsItemCount(IRibbonControl ribbonControl)
        {
            int noOfItems = 0;
            try
            {
                if (ribbonControl == null)
                {
                    throw new ArgumentNullException("ribbonControl");
                }

                // use a list to match index to connection as it will be used in the 
                // other callback for the connections drop-down list
                // as the Connections dictionary can't be index by integer
                this.connectionsList = new List<TridentConnection>();
                foreach (KeyValuePair<string, TridentConnection> connection in this.controller.UserData.Connections)
                {
                    this.connectionsList.Add(connection.Value);
                }

                noOfItems = this.connectionsList.Count;
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowException(ex, null);
            }

            return noOfItems;
        }

        /// <summary>
        /// Callback for the Shows the connectionsList button
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="index">The index.</param>
        /// <returns>string</returns>
        public string GetConnectionsDropDownLabel(IRibbonControl ribbonControl, int index)
        {
            string label = string.Empty;
            try
            {
                if (ribbonControl == null)
                {
                    throw new ArgumentNullException("ribbonControl");
                }

                label = this.connectionsList[index].Name;
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowException(ex, null);
            }

            return label;
        }

        /// <summary>
        /// Callback caled for each item in drop down (This provides the index for the connection name)
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <returns>int</returns>
        public int GetConnectionsSelectedIndex(IRibbonControl ribbonControl)
        {
            int index = 0;

            try
            {
                if (this.controller.DocumentsCount == 0)
                {
                    return index;
                }

                if (ribbonControl == null)
                {
                    throw new ArgumentNullException("ribbonControl");
                }

                //Get the Active Connection from Stored location for current Document
                string connectionName = this.controller.CurrentConnectionName;
                if (string.IsNullOrEmpty(connectionName))
                {
                    connectionName = this.controller.UserData.CurrentConnectionName;
                    this.controller.CurrentConnectionName = connectionName;
                }

                if (string.IsNullOrEmpty(connectionName) && this.connectionsList.Count > 0)
                {
                    this.SetConnectionFormIndex(0);
                }
                else
                {
                    foreach (TridentConnection connection in this.connectionsList)
                    {
                        if (connection.Name.Equals(connectionName))
                        {
                            break;
                        }

                        index++;
                    }
                }
            }
            catch (Exception ex)
            {
                MessageBoxHelper.ShowException(ex, null);
            }

            return index;
        }

        /// <summary>
        /// Called when [dialog open event].
        /// </summary>
        /// <param name="ribbonControl">The ribbon control.</param>
        /// <param name="cancelDefault">if set to <c>true</c> [cancel default].</param>
        public void OnDialogOpenEvent(IRibbonControl ribbonControl, ref bool cancelDefault)
        {
            cancelDefault = false;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Gets the resource string.
        /// </summary>
        /// <param name="resourceName">Name of the resource.</param>
        /// <returns>string</returns>
        private static string GetResourceString(string resourceName)
        {
            string result = ResourceFetcher.GetString(resourceName);
            if (result == null)
            {
                Trace.WriteLine("Resource string not found for name = " + resourceName, TraceLevel.Error.ToString());
                result = resourceName;
            }

            return result;
        }

        /// <summary>
        /// Sets the index of the connection form.
        /// </summary>
        /// <param name="selectedIndex">Index of the selected.</param>
        private void SetConnectionFormIndex(int selectedIndex)
        {
            TridentConnection connection = this.connectionsList[selectedIndex];
            if (connection != null)
            {
                TridentConnection connectionReturn = new TridentConnection();
                if (this.controller.TestConnectionAndPromptIfRequired(connection, ref connectionReturn, true))
                {
                    this.SetCurrentConnection(connectionReturn);
                }
                else
                {
                    this.RemoveConnection(connection);
                    this.SetNextDefaultConnection();
                }
            }
        }

        /// <summary>
        /// Checks for need to invalidate the ribbon control.
        /// </summary>
        /// <param name="command">The command.</param>
        /// <param name="controlId">The control id.</param>
        private void CheckForNeedToInvalidate(RibbonCommand command, string controlId)
        {
            if (command.NeedsInvalidate)
            {
                this.ribbonUI.InvalidateControl(controlId);
            }
        }

        /// <summary>
        /// Sets the next default connection.
        /// </summary>
        private void SetNextDefaultConnection()
        {
            TridentConnection newConnection = null;
            if (!this.controller.UserData.CurrentConnectionName.IsNullOrEmpty())
            {
                newConnection = this.controller.UserData.Connections[this.controller.UserData.CurrentConnectionName];
            }

            if (newConnection == null)
            {
                IEnumerator iterator = this.connectionsList.GetEnumerator();
                iterator.MoveNext();
                newConnection = iterator.Current as TridentConnection;
            }

            if (newConnection == null)
            {
                this.SetCurrentConnection(null);
                return;
            }

            TridentConnection connectionReturn = new TridentConnection();
            if (this.controller.TestConnectionAndPromptIfRequired(newConnection, ref connectionReturn, true))
            {
                this.SetCurrentConnection(connectionReturn);
                return;
            }
            else
            {
                this.RemoveConnection(newConnection);
                this.SetNextDefaultConnection();
            }
        }

        /// <summary>
        /// Sets the current connection.
        /// </summary>
        /// <param name="connection">The connection.</param>
        private void SetCurrentConnection(TridentConnection connection)
        {
            string connectionName = connection == null ? string.Empty : connection.Name;
            this.controller.UserData.CurrentConnectionName = connectionName;
            this.controller.CurrentConnection = connection;
            this.controller.ConnectionChanged(true, ConnectionOperation.Changed);
        }

        /// <summary>
        /// Removes the connection.
        /// </summary>
        /// <param name="connection">The connection.</param>
        private void RemoveConnection(TridentConnection connection)
        {
            if (connection == null)
            {
                return;
            }

            this.connectionsList.Remove(connection);
            this.controller.RemoveConnection(connection.Name);
            this.controller.ConnectionChanged(false, ConnectionOperation.Deleted);
        }

        #endregion
    }
}
